require 'digest/sha1'

class User < ActiveRecord::Base
  include Authentication
  include Authentication::ByPassword
  include Authentication::ByCookieToken

  DEFAULT_ID = 0
  
  apply_simple_captcha
  acts_as_taggable
  has_many_friends
  
 	has_and_belongs_to_many :roles
  has_many :features
  has_one :avatar, :as=>:assetable, :dependent=>:destroy, :conditions=>{:is_main=>true}
  has_many :avatars, :as=>:assetable, :dependent=>:destroy, :order=>"filename"
  has_many :memberships, :dependent => :destroy
  has_many :communities, :through=>:memberships, :source=>:community
  has_many :posts, :order=>"posts.created_at DESC"
  belongs_to :country
  has_many :pages
	has_many :events
	has_many :rss_channels
	has_many :articles
	
	acts_as_enum :gender, [:men, :women, :undefined]

  validates_presence_of     :login
  validates_length_of       :login,    :within => 3..40
  validates_uniqueness_of   :login
  validates_format_of       :login,    :with => /\A[a-zA-Z]+\z/, :message => "только латиница"

  validates_format_of       :name,     :with => Authentication.name_regex,  :message => Authentication.bad_name_message, :allow_nil => true
  validates_length_of       :name,     :maximum => 100

  validates_presence_of     :email
  validates_length_of       :email,    :within => 6..100 #r@a.wk
  validates_uniqueness_of   :email
  validates_format_of       :email,    :with => Authentication.email_regex, :message => Authentication.bad_email_message

  before_create :make_activation_code, :set_default_role

  # HACK HACK HACK -- how to do attr_accessible from here?
  # prevents a user from submitting a crafted form that bypasses activation
  # anything else you want your user to change should be added here.
  attr_accessible :login, :email, :name, :password, :password_confirmation, :icq, :url, :birthday, :gender
  attr_accessible :captcha_key, :captcha, :city_name, :country_id, :description, :status, :tag_list, :region
  
  def user_css
  	case self.gender
  		when :women then 'user'
  		when :men then 'userBlue'
  		else 'userGray'
  	end
  end
  
  def gender_tag
  	case self.gender
  		when :women
  			return 'female'
  		when :men
  			return 'male'
  		else
  			return 'unknown'
  	end
  end
	
	def relative_posts(page)
		user_posts_ids = community_posts_ids = ids_user = ids_comm = []
		self.events.each do |e|
			ids_comm << e.eventable_id if e.eventable_type == 'Community'
			ids_user << e.eventable_id if e.eventable_type == 'User'
		end
		
		community_posts_ids = Post.find(:all, :select=>"posts.id", :conditions=>"community_id in (#{ids_comm.join(',')})").map{|p| p.id} unless ids_comm.size == 0
		user_posts_ids = Post.find(:all, :select=>"posts.id", :conditions=>"user_id in (#{ids_user.join(',')})").map{|p| p.id} unless ids_user.size == 0
		posts_ids = community_posts_ids + user_posts_ids
		posts_ids.uniq!
		posts_ids.size == 0 ? [] : Post.paginate(:all, :conditions=>"id in (#{posts_ids.join(',')})", :page=>page, :per_page=>20, :order=>"created_at DESC")
	end
	
	def last_comments
		ids = self.posts.find(:all, :select=>"posts.id").map(&:id)
		return [] if ids.empty?
		Comment.find(:all, :conditions=>"commentable_id in (#{ids.join(',')}) AND commentable_type = 'Post' AND user_id <> #{self.id}", :limit=>3, :order=>"created_at DESC")
	end

	def rss_posts(rss_ch, page, per_page)
		posts = []
		rss_ch.each do |rss|
			posts += rss.post
		end
		posts.sort! {|x,y| y.date<=>x.date}
		posts.to_paginate(page, per_page)
	end
	
	def join_community? (community)
		self.communities.find_by_id(community.id)
	end

	def title
		self.name.blank? ? self.login : self.login 	
	end

	def to_param
		self.login
	end
	
	def theme
		read_attribute(:theme) || 'default'
	end
	
	def admin?
  	has_role?(Role::ADMIN)
  end
	  
  def moderator?
  	has_role?(Role::MODERATOR)
  end
  
  def default?
  	has_role?(Role::DEFAULT)
  end
  
  def has_role?(ident)
  	roles.any?{ |r| r.ident == ident }
  end
  
  def set_default_role
  	return unless roles.empty?
  	roles << Role.find_by_ident(Role::DEFAULT)
  end
  
  def new_roles=(ids_roles)
  	return if ids_roles.blank?
  	ids_roles.map!(&:to_i)
  	roles.reject(&:new_record?).each do |role|
  		roles.delete(role) unless ids_roles.include?(role.id)
		end
		
		ids_new = ids_roles - roles.map(&:id)
		
		ids_new.each do |r|
			roles << Role.find(r) unless r.to_i == 0 
		end
  end

  def save_roles
    roles.each do |role|
      role.save(false)
    end
  end
	

  # Activates the user in the database.
  def activate!
    @activated = true
    self.activated_at = Time.now.utc
    self.activation_code = nil
    save(false)
  end

  # Returns true if the user has just been activated.
  def recently_activated?
    @activated
  end

  def active?
    # the existence of an activation code means they have not activated yet
    activation_code.nil?
  end

  # Authenticates a user by their login name and unencrypted password.  Returns the user or nil.
  #
  # uff.  this is really an authorization, not authentication routine.  
  # We really need a Dispatch Chain here or something.
  # This will also let us return a human error message.
  #
  def self.authenticate(login, password)
    return nil if login.blank? || password.blank?
    u = find :first, :conditions => ['login = ? and activated_at IS NOT NULL', login] # need to get the salt
    u && u.authenticated?(password) ? u : nil
  end

  def login=(value)
    write_attribute :login, (value ? value.downcase : nil)
  end

  def email=(value)
    write_attribute :email, (value ? value.downcase : nil)
  end

  protected
    
    def make_activation_code
        self.activation_code = self.class.make_token
    end


end
